package faceengine

/*
#cgo CFLAGS		: -I./include
#cgo LDFLAGS	: -larcsoft_face_engine
#include <stdlib.h>
#include "merror.h"
#include "asvloffscreen.h"
#include "arcsoft_face_sdk.h"
*/
import "C"
import (
	"unsafe"
)

// FaceEngine 引擎结构体
type FaceEngine struct {
	handle C.MHandle
}

// 人脸信息结构体
type FaceInfo struct {
	FaceNum int32 // 检测到的人脸个数
	//下面都是第一个人信息
	FaceRect             Rect  // 人脸框信息
	WearGlasses          int32 // 戴眼镜状态, 0 未戴眼镜；1 戴眼镜；2 墨镜
	LeftEyeOpen          int32 // 左眼状态 0 闭眼；1 睁眼
	RightEyeOpen         int32 // 右眼状态 0 闭眼；1 睁眼
	MouthClose           int32 // 张嘴状态 0 张嘴；1 合嘴
	FaceIsWithinBoundary int32 // 人脸是否在边界内 0 人脸溢出；1 人脸在图像边界内
	Roll                 float32
	Yaw                  float32
	Pitch                float32
	IsLive               int32 // 0:非真人 1:真人 -1:不确定 -2:传入人脸数>1
	IsMask               int32 // 0代表没有带口罩 1代表带口罩 -1表示不确定
}

// Rect 人脸坐标结构体
type Rect struct {
	Left   int32
	Top    int32
	Right  int32
	Bottom int32
}

// ImageData 对应ASVLOFFSCREEN结构体
type ImageData struct {
	PixelArrayFormat C.MUInt32
	Width            int
	Height           int
	ImageData        [4][]uint8
	WidthStep        [4]int
}

// EngineError SDK错误码
type EngineError struct {
	Code int
	Text string
}

const (
	// DetectModeVideo 视频模式
	DetectModeVideo = C.ASF_DETECT_MODE_VIDEO
	// DetectModeImage 图片模式
	DetectModeImage = C.ASF_DETECT_MODE_IMAGE
	// OrientPriority0 不旋转
	OrientPriority0 = C.ASF_OP_0_ONLY
	// OrientPriority90 旋转90度
	OrientPriority90 = C.ASF_OP_90_ONLY
	// OrientPriority270 旋转270度
	OrientPriority270 = C.ASF_OP_270_ONLY
	// OrientPriority180 旋转180度
	OrientPriority180 = C.ASF_OP_180_ONLY
	// OrientPriorityAllOut 角度不限
	OrientPriorityAllOut = C.ASF_OP_ALL_OUT
	// EnableFaceDetect 开启人脸检测
	EnableFaceDetect = C.ASF_FACE_DETECT
	// EnableFaceRecognition 开启人脸识别
	EnableFaceRecognition = C.ASF_FACERECOGNITION
	// EnableLiveness 开启活体检测
	EnableLiveness = C.ASF_LIVENESS
	// EnableImageQuality 开启单人脸图片质量检测
	EnableImageQuality = C.ASF_IMAGEQUALITY
	// EnableMaskDetect 开启口罩检测
	EnableMaskDetect = C.ASF_MASKDETECT
	// EnableUpdateFaceData 开启人脸数据更新
	EnableUpdateFaceData = C.ASF_UPDATE_FACEDATA
	ColorFormatBGR24     = C.ASVL_PAF_RGB24_B8G8R8
)

// OnlineActivation 在线激活接口
func OnlineActivation(appID, sdkKey, activeKey string) (err error) {
	id := C.CString(appID)
	sk := C.CString(sdkKey)
	ak := C.CString(activeKey)
	defer func() {
		C.free(unsafe.Pointer(id))
		C.free(unsafe.Pointer(sk))
		C.free(unsafe.Pointer(ak))
	}()
	r := C.ASFOnlineActivation(id, sk, ak)
	if r != C.MOK && r != C.MERR_ASF_ALREADY_ACTIVATED {
		err = newError(int(r), "激活SDK失败")
	}
	return
}

// NewFaceEngine 创建一个新的引擎实例
// 如果调用初始化函数失败则返回一个错误
func NewFaceEngine() (*FaceEngine, error) {
	engine, err := &FaceEngine{}, error(nil)
	r := C.ASFInitEngine(DetectModeImage, OrientPriority0,
		10, EnableFaceDetect|EnableFaceRecognition|
			EnableLiveness|EnableImageQuality|EnableMaskDetect|
			EnableUpdateFaceData, &engine.handle)
	if r != C.MOK {
		err = newError(int(r), "初始化引擎失败")
	}
	return engine, err
}

// DetectFaces 人脸检测，目前不支持IR图像数据检测
func (engine *FaceEngine) DetectFaces(
	width int, // 宽度
	height int, // 高度
	format C.MInt32, // 图像格式
	imgData []byte, // 图片数据
) (faceInfo FaceInfo, err error) {
	asfFaceInfo := &C.ASF_MultiFaceInfo{}
	r := C.ASFDetectFaces(engine.handle,
		C.MInt32(width),
		C.MInt32(height),
		format,
		(*C.MUInt8)(unsafe.Pointer(&imgData[0])),
		asfFaceInfo,
		C.ASF_DETECT_MODEL_RGB,
	)
	if r != C.MOK {
		return faceInfo, newError(int(r), "人脸检测:ASFDetectFacesEx失败")
	}

	faceNum := int32(asfFaceInfo.faceNum)
	faceInfo.FaceNum = faceNum
	if faceInfo.FaceNum < 1 {
		return faceInfo, nil
	}

	faceInfo.FaceRect = (*[10]Rect)(unsafe.Pointer(asfFaceInfo.faceRect))[0]
	faceInfo.FaceIsWithinBoundary = *(*int32)(unsafe.Pointer(asfFaceInfo.faceIsWithinBoundary))
	faceInfo.WearGlasses = *(*int32)(unsafe.Pointer(asfFaceInfo.faceAttributeInfo.wearGlasses))
	faceInfo.LeftEyeOpen = *(*int32)(unsafe.Pointer(asfFaceInfo.faceAttributeInfo.leftEyeOpen))
	faceInfo.RightEyeOpen = *(*int32)(unsafe.Pointer(asfFaceInfo.faceAttributeInfo.rightEyeOpen))
	faceInfo.MouthClose = *(*int32)(unsafe.Pointer(asfFaceInfo.faceAttributeInfo.mouthClose))
	faceInfo.Roll = *(*float32)(unsafe.Pointer(asfFaceInfo.face3DAngleInfo.roll))
	faceInfo.Yaw = *(*float32)(unsafe.Pointer(asfFaceInfo.face3DAngleInfo.yaw))
	faceInfo.Pitch = *(*float32)(unsafe.Pointer(asfFaceInfo.face3DAngleInfo.pitch))

	//获取活人和口罩前必须调用该函数
	r = C.ASFProcess(engine.handle,
		C.MInt32(width),
		C.MInt32(height),
		format,
		(*C.MUInt8)(unsafe.Pointer(&imgData[0])),
		asfFaceInfo,
		EnableMaskDetect|EnableLiveness)
	if r != C.MOK {
		return faceInfo, newError(int(r), "人脸检测:ASFProcessEx失败")
	}

	//获取活人
	asfLivenessInfo := &C.ASF_LivenessInfo{}
	r = C.ASFGetLivenessScore((C.MHandle)(engine.handle), asfLivenessInfo)
	if r != C.MOK {
		return faceInfo, newError(int(r), "人脸检测:ASFGetLivenessScore失败")
	}
	faceInfo.IsLive = (*[10]int32)(unsafe.Pointer(asfLivenessInfo.isLive))[0]

	//获取口罩
	asfMaskInfo := &C.ASF_MaskInfo{}
	r = C.ASFGetMask(engine.handle, (*C.ASF_MaskInfo)(unsafe.Pointer(asfMaskInfo)))
	if r != C.MOK {
		return faceInfo, newError(int(r), "人脸检测:ASFGetMask失败")
	}
	faceInfo.IsMask = (*[10]int32)(unsafe.Pointer(asfMaskInfo.maskArray))[0]

	return
}

// DetectFacesEx 检测人脸信息
// 该接口与 DetectFaces 功能一致，但采用结构体的形式传入图像数据，对更高精度的图像兼容性更好。
func (engine *FaceEngine) DetectFacesEx(imageData ImageData) (faceInfo FaceInfo, err error) {
	asfFaceInfo := &C.ASF_MultiFaceInfo{}
	offscreen := imageDataToASVLOFFSCREEN(imageData)

	r := C.ASFDetectFacesEx(engine.handle, offscreen, asfFaceInfo, C.ASF_DETECT_MODEL_RGB)
	if r != C.MOK {
		return faceInfo, newError(int(r), "人脸检测:ASFDetectFacesEx失败")
	}

	faceNum := int32(asfFaceInfo.faceNum)
	faceInfo.FaceNum = faceNum
	if faceInfo.FaceNum < 1 {
		return faceInfo, nil
	}

	faceInfo.FaceRect = (*[10]Rect)(unsafe.Pointer(asfFaceInfo.faceRect))[0]
	faceInfo.FaceIsWithinBoundary = *(*int32)(unsafe.Pointer(asfFaceInfo.faceIsWithinBoundary))
	faceInfo.WearGlasses = *(*int32)(unsafe.Pointer(asfFaceInfo.faceAttributeInfo.wearGlasses))
	faceInfo.LeftEyeOpen = *(*int32)(unsafe.Pointer(asfFaceInfo.faceAttributeInfo.leftEyeOpen))
	faceInfo.RightEyeOpen = *(*int32)(unsafe.Pointer(asfFaceInfo.faceAttributeInfo.rightEyeOpen))
	faceInfo.MouthClose = *(*int32)(unsafe.Pointer(asfFaceInfo.faceAttributeInfo.mouthClose))
	faceInfo.Roll = *(*float32)(unsafe.Pointer(asfFaceInfo.face3DAngleInfo.roll))
	faceInfo.Yaw = *(*float32)(unsafe.Pointer(asfFaceInfo.face3DAngleInfo.yaw))
	faceInfo.Pitch = *(*float32)(unsafe.Pointer(asfFaceInfo.face3DAngleInfo.pitch))

	//获取活人和口罩前必须调用该函数
	r = C.ASFProcessEx(engine.handle, offscreen, asfFaceInfo,
		EnableMaskDetect|EnableLiveness)
	if r != C.MOK {
		return faceInfo, newError(int(r), "人脸检测:ASFProcessEx失败")
	}

	//获取活人
	asfLivenessInfo := &C.ASF_LivenessInfo{}
	r = C.ASFGetLivenessScore((C.MHandle)(engine.handle), asfLivenessInfo)
	if r != C.MOK {
		return faceInfo, newError(int(r), "人脸检测:ASFGetLivenessScore失败")
	}
	faceInfo.IsLive = (*[10]int32)(unsafe.Pointer(asfLivenessInfo.isLive))[0]

	//获取口罩
	asfMaskInfo := &C.ASF_MaskInfo{}
	r = C.ASFGetMask(engine.handle, (*C.ASF_MaskInfo)(unsafe.Pointer(asfMaskInfo)))
	if r != C.MOK {
		return faceInfo, newError(int(r), "人脸检测:ASFGetMask失败")
	}
	faceInfo.IsMask = (*[10]int32)(unsafe.Pointer(asfMaskInfo.maskArray))[0]

	return
}

// Destroy 销毁引擎
func (engine *FaceEngine) Destroy() (err error) {
	r := C.ASFUninitEngine(engine.handle)
	if r != C.MOK {
		err = newError(int(r), "销毁引擎失败")
	}
	return
}

// 实现Error接口
func (err EngineError) Error() string {
	return err.Text
}

func newError(code int, text string) EngineError {
	return EngineError{
		Code: code,
		Text: text,
	}
}

func imageDataToASVLOFFSCREEN(imageData ImageData) C.LPASVLOFFSCREEN {
	var pi32Pitch [4](C.MInt32)
	for i := 0; i < 4; i++ {
		pi32Pitch[i] = C.MInt32(imageData.WidthStep[0])
	}
	var ppu8Plane [4](*C.MUInt8)
	for i := 0; i < 4; i++ {
		if len(imageData.ImageData[i]) > 0 {
			ppu8Plane[i] = (*C.MUInt8)(unsafe.Pointer(&imageData.ImageData[i][0]))
		}
	}
	return &C.ASVLOFFSCREEN{
		imageData.PixelArrayFormat,
		C.MInt32(imageData.Width),
		C.MInt32(imageData.Height),
		ppu8Plane,
		pi32Pitch}
}
